﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Infrastructure.ObjectModel;
using B10_Ex02_Elad_034695114_Meyron_026607788;
using B10_Ex02_Elad_034695114_Meyron_026607788.ObjectModel;

namespace B10_Ex02_EladLebovitch_034695114.ObjectModel
{
    public class EnemyMatrix : RegisteredComponent
    {
        #region Consts
        private const int k_RedPoints = 250;
        private const int k_TealPoints = 120;
        private const int k_YellowPoints = 60;
        private const int k_NumOfEnemiesTextures = 3;
        private const int k_EnemyPixelWidth = 32;
        private const int k_EnemyPixelHeight = 32;
        private const float k_EnemyPixelMagin = 0.6F;
        private const float k_LineShortenInterval = 0.93F;
        private const float k_EnemyKilledShortenInterval = 0.91F;
        private const int k_EnemyHorizontalMove = k_EnemyPixelWidth / 2;
        private const int k_EnemyVerticalMove = k_EnemyPixelHeight / 2;
        #endregion

        #region Members

        private int m_EnemiesInRow;
        private int m_EnemiesInCol;
        private int m_EnemyMoveTimesToDir;
        private int m_NumberOfEnemyCurrentMoves = 0;
        private int m_EnemyMoveDirection = 1;
        private float m_EnemyMoveInterval = 500;
        private TimeSpan m_LastEnemyMoveTime;
        private EnemySprite[,] m_MatEnemies;
        
        #endregion

        #region Properties

        public List<Sprite> Bullets
        {
            get;
            protected set;
        }

        public List<Sprite> EnemiesList
        {
            get;
            protected set;
        }

        public float EnemyMoveInterval
        {
            get
            {
                return m_EnemyMoveInterval;
            }

            set
            {
                m_EnemyMoveInterval = value;
            }
        }

        public int EnemiesInRow
        {
            get
            {
                return m_EnemiesInRow;
            }

            set
            {
                m_EnemiesInRow = value;
            }
        }

        public int EnemiesInCol
        {
            get
            {
                return m_EnemiesInCol;
            }

            set
            {
                m_EnemiesInCol = value;
            }
        }

        /// <summary>
        /// Where the lower bound of matrix is
        /// </summary>
        public int LowerBoundery
        {
            get
            {
                return (int)EnemiesList.Max(sprt => sprt.Position.Y + sprt.Texture.Height);
            }
        }

        #endregion

        #region CTor
        /// <summary>
        /// Creates an enemy matrix
        /// </summary>
        /// <param name="i_game">The owning game</param>
        /// <param name="i_EnemiesInRow">Enemies in row</param>
        /// <param name="i_EnemiesInCol">Enemies in col</param>
        public EnemyMatrix(Game i_game, int i_EnemiesInRow, int i_EnemiesInCol)
                        : base(i_game)
        {
            // Init members
            EnemiesInRow = i_EnemiesInRow;
            EnemiesInCol = i_EnemiesInCol;
            m_MatEnemies = new EnemySprite[EnemiesInCol, EnemiesInRow];

            // Create the enemies in the matrix
            createEnemyRow(0, @"Sprites\Enemy0101_32x32", Color.Red, k_RedPoints);
            createEnemyRow(1, @"Sprites\Enemy0201_32x32", Color.Teal, k_TealPoints);
            createEnemyRow(2, @"Sprites\Enemy0201_32x32", Color.Teal, k_TealPoints);
            createEnemyRow(3, @"Sprites\Enemy0301_32x32", Color.YellowGreen, k_YellowPoints);
            createEnemyRow(4, @"Sprites\Enemy0301_32x32", Color.YellowGreen, k_YellowPoints);

            this.EnemiesList = new List<Sprite>();
            this.Bullets = new List<Sprite>();
            for (int cols = 0; cols < i_EnemiesInCol; cols++)
            {
                for (int rows = 0; rows < i_EnemiesInRow; rows++)
                {
                    this.EnemiesList.Add(this.m_MatEnemies[cols, rows]);
                }   
            }

            // Calc the number of jumps that will be made to each dir
            m_EnemyMoveTimesToDir = getNumberOfMovesUntilDown();
        } 
        #endregion

        #region Methods
        /// <summary>
        /// Creates an enemy row
        /// </summary>
        /// <param name="i_CurrRow">Which row to create</param>
        /// <param name="i_strTextureName">the name of the texture to creatre</param>
        /// <param name="i_clrEnemyColor">the color of the enemies</param>
        private void createEnemyRow(int i_CurrRow, string i_strTextureName, Color i_clrEnemyColor, int i_points)
        {
            // Set starting point for the row so that row will be centered
            float posX = k_EnemyPixelMagin * k_EnemyPixelWidth;
            float posY = (k_EnemyPixelHeight * 3) + ((i_CurrRow * k_EnemyPixelHeight) * k_EnemyPixelMagin);

            // Go through the entire row
            for (int currCol = 0; currCol < this.EnemiesInRow; currCol++)
            {
                // Set new enemy
                EnemySprite enmyCurr = new EnemySprite((Invadors)this.Game, i_strTextureName, i_clrEnemyColor, i_points);
                this.m_MatEnemies[i_CurrRow, currCol] = enmyCurr;
                enmyCurr.Position = new Vector2(
                                                posX + (currCol * k_EnemyPixelWidth),
                                                posY + (i_CurrRow * k_EnemyPixelHeight));

                // Update next enemy point for this row
                posX += k_EnemyPixelWidth * k_EnemyPixelMagin;

                // register in the game:
                this.Game.Components.Add(enmyCurr);
            }
        }

        /// <summary>
        /// Updates positions
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            // Check if we need to move the enemies
            if (TimeSpan.Compare(
                gameTime.TotalGameTime,
                m_LastEnemyMoveTime.Add(TimeSpan.FromMilliseconds(m_EnemyMoveInterval))) > 0)
            {
                // Use this float to modify vertical pos
                float verticalMove = 0;

                // Check if we moved too much
                if (m_NumberOfEnemyCurrentMoves >= m_EnemyMoveTimesToDir)
                {
                    // Switch directions, init counters
                    m_NumberOfEnemyCurrentMoves = 0;
                    m_EnemyMoveDirection *= -1;
                    verticalMove = k_EnemyVerticalMove;
                    EnemyMoveInterval *= k_LineShortenInterval;
                }

                // Move the enemies
                moveEnemyMatrix(verticalMove);

                // We made a move, save the time and update the numbers 
                // so we know when to make the next move
                m_NumberOfEnemyCurrentMoves++;
                m_LastEnemyMoveTime = gameTime.TotalGameTime;
            }

            UpdateBullets();

            // Should be called anyway
            base.Update(gameTime);
        }

        /// <summary>
        /// Updates the current bullets
        /// </summary>
        protected void UpdateBullets()
        {
            this.Bullets.Clear();

            foreach (EnemySprite enmCurr in this.EnemiesList)
            {
                this.Bullets.AddRange(enmCurr.Bullets.Select(blt => blt as Sprite));
            }
        }

        /// <summary>
        /// Moves the enemies according to parameters
        /// </summary>
        /// <param name="verticalMove">Amount to move vertically</param>
        private void moveEnemyMatrix(float verticalMove)
        {
            Vector2 vcNewPos;

            // Go through all the enemies
            foreach (EnemySprite enmyCurr in this.m_MatEnemies)
            {
                // Update position
                vcNewPos = enmyCurr.Position;
                vcNewPos.Y += verticalMove;
                vcNewPos.X += k_EnemyHorizontalMove * m_EnemyMoveDirection;
                enmyCurr.Position = vcNewPos;
            }
        }

        /// <summary>
        /// Checks how many times will the enemies move in each direction
        /// </summary>
        /// <returns>The result</returns>
        private int getNumberOfMovesUntilDown()
        {
            // Get the last enemy
            EnemySprite enmyLast = m_MatEnemies[m_MatEnemies.GetUpperBound(0), m_MatEnemies.GetUpperBound(1)];
            
            // Check the distance from the end of the screen
            float pxlAmountFromEnd = this.Game.GraphicsDevice.Viewport.Width - 
                                    (enmyLast.Position.X + k_EnemyHorizontalMove);
            
            // Calc the number of jumps to be made
            int numberOfJumps = Convert.ToInt32(pxlAmountFromEnd / k_EnemyHorizontalMove);
                        
            // Make one less jump so we don't leave the screen
            numberOfJumps -= 2;

            return numberOfJumps;
        }

        /// <summary>
        /// Allows access to the enemySprites by index
        /// </summary>
        /// <param name="i_EnemiesInRow">Row</param>
        /// <param name="i_EnemiesInCol">Col</param>
        /// <returns>Thw EnemySprite</returns>
        public EnemySprite GetEnemyAt(int i_EnemiesInRow, int i_EnemiesInCol)
        {
            return m_MatEnemies[i_EnemiesInRow, i_EnemiesInCol];
        }

        /// <summary>
        /// Kills the enemy at index
        /// </summary>
        /// <param name="i_EnemiesInRow">Row</param>
        /// <param name="i_EnemiesInCol">Col</param>
        public void KillEnemyAt(int i_EnemiesInRow, int i_EnemiesInCol)
        {
            EnemySprite enemy = GetEnemyAt(i_EnemiesInRow, i_EnemiesInCol);
            enemy.Distroy();
            this.EnemiesList.Remove(enemy);
        }

        /// <summary>
        /// Kills the enemy 
        /// </summary>
        /// <param name="i_EnemiesInRow">Row</param>
        /// <param name="i_EnemiesInCol">Col</param>
        public void KillEnemy(EnemySprite i_enemySprite)
        {
            i_enemySprite.Distroy();
            this.EnemiesList.Remove(i_enemySprite);
            
            EnemyMoveInterval *= k_EnemyKilledShortenInterval;
        }

        #endregion
    }
}
